using System;
using System.Collections.Generic;
using Evolution.Engine.Managers;
using System.CodeDom.Compiler;
using System.Reflection;
using System.Security.Permissions;
using Microsoft.CSharp;

namespace Evolution.Engine.Utilities
{
    public class CodeCompiler : Singleton<CodeCompiler>
    {
        #region Fields

        private CompilerParameters _Parameters = new CompilerParameters();
        private CodeDomProvider _Provider = new CSharpCodeProvider();

        #endregion

        #region Properties

        public bool GenerateAssembly
        {
            get { return _Parameters.GenerateExecutable; }
            set { _Parameters.GenerateExecutable = value; }
        }
        public bool GenerateInMemory
        {
            get { return _Parameters.GenerateInMemory; }
            set { _Parameters.GenerateInMemory = value; }
        }
        public bool GenerateDebugInformation
        {
            get { return _Parameters.IncludeDebugInformation; }
            set { _Parameters.IncludeDebugInformation = value; }
        }
        public string OutputAssebly
        {
            get { return _Parameters.OutputAssembly; }
            set { _Parameters.OutputAssembly = value; }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Loads an assembly from file into and returns the loaded assembly
        /// </summary>
        /// <param name="AssemblyName"></param>
        /// <returns>System.Reflection.Assembly</returns>
        [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
        public Assembly LoadAssembly(string AssemblyName)
        {
            return Assembly.LoadFile(AssemblyName);
        }

        /// <summary>
        /// Initializes the compiler with all default options specified. CSharp, In memory only compile
        /// </summary>
        public CodeCompiler()
        {
            Initialize();
        }

        /// <summary>
        /// Initializes the Compiler, based on default settings
        /// </summary>
        private void Initialize()
        {
            GenerateAssembly = false;
            GenerateInMemory = true;
            GenerateDebugInformation = false;

            AddDefaultAssemblies();
        }

        /// <summary>
        /// Compiles the given script
        /// </summary>
        /// <param name="Source"></param>
        /// <returns></returns>
        public CompilerResults CompileCode(string CodeName, string Group)
        {
            return _Provider.CompileAssemblyFromSource(_Parameters, 
                (string)ResourceManager.Instance.GetResource(CodeName, Group)
            );
        }

        /// <summary>
        /// Finds the appropriate Interface in the given assembly
        /// </summary>
        /// <param name="DLL"></param>
        /// <param name="InterfaceName"></param>
        /// <returns></returns>
        public object FindInterface(Assembly DLL, string InterfaceName)
        {
            // Loop through types looking for one that implements the given interface
            foreach (Type t in DLL.GetTypes())
            {
                if (t.GetInterface(InterfaceName, true) != null)
                    return DLL.CreateInstance(t.FullName);
            }

            return null;
        }

        /// <summary>
        /// Adds an assembly to the Assembly chain for the Current Script to be compiled
        /// </summary>
        /// <param name="Reference"></param>
        public void AddAssemblyReference(string Reference)
        {
            _Parameters.ReferencedAssemblies.Add(Reference);
        }

        /// <summary>
        /// Will add the default assemblies: System, System.Data, System.Xml
        /// </summary>
        private void AddDefaultAssemblies()
        {
            AddAssemblyReference(@"System.dll");
            AddAssemblyReference(@"System.Xml.dll");
            AddAssemblyReference(@"Evolution.Engine.dll");
            AddAssemblyReference(@"Microsoft.Xna.Framework.dll");
            AddAssemblyReference(@"Microsoft.Xna.Framework.Game.dll");
        }

        #endregion
    }
}

